import {
  observe,
  reaction
} from 'mobx';
import LocalObject from "./LocalObject"
import * as _types from "./types"

/* 42 */
/***/
var scratchArrayForArrayAssign = [];

function arrayAssign(l1, l2, r2Tor1, equals) {
  if (!Array.isArray(l1.d)) {
    l1.d = [];
  }
  var r1 = l1.d;
  var r2 = l2.d;

  scratchArrayForArrayAssign.length = r2.length;
  var r2_ = r2Tor1 ? r2Tor1(r2, scratchArrayForArrayAssign) : r2;
  var isEqual = equals ? equals(r1, r2) : r2_.length === r1.length && r2_.every(function(e, index) {
    return e === r1[index];
  });
  if (!isEqual) {
    // r1.splice(0, r1.length, ...r2_);
    // r1.length = r2_.length; // 去掉多余元素
    if (l1.type === 'vue') {
      if (r2_.some(function(e, i) {
          return l1.d[i] !== e;
        })) {
        var _l1$d;
        (_l1$d = l1.d).splice.apply(_l1$d, [0, l1.d.length, ...r2_]);
      }
    } else {
      // 这个操作会导致部分binPosition出现问题，纬度突然由弧度变成了角度，引起一些列错误变化
      // 它的每一次element赋值操作都会引起一次l1.d的observe，之后就出问题了
      // 致于为什么observe之后会导致弧度变角度，还不清楚，只是发现改成splice的方式以后就好了
      // splice会引起一次observe，但是observe中如果发现增删的数值相同，即不发生任何变化时，会直接return。
      // r2_.forEach((e, i) => (l1.d[i] !== e) && (l1.d[i] = e));
      if (r2_.some(function(e, i) {
          return l1.d[i] !== e;
        })) {
        var _l1$d2;
        (_l1$d2 = l1.d).splice.apply(_l1$d2, [0, l1.d.length, ...r2_]);
      }
    }
  }
}

function assign(l1, l2, r2Tor1, equals) {
  var l1d = l1.d;
  var l2d = l2.d;
  var newValue = r2Tor1 ? r2Tor1(l2d) : l2d;
  var equal = equals ? equals(l1d, l2d) : l1d === newValue;
  if (!equal) {
    l1.d = newValue;
  }
}

var scratchEqualArray = [];

/**
 * MVVM单向绑定
 * @memberof MVVM
 * @description 单向绑定的属性，reactObject1[reactProperty1]将跟踪reactObject2[reactProperty2]，但reactObject1发生变化reactObject2并不会跟踪！
 * @param {object} reactObject1 第一个绑定对象，可以是vue对象，或者是XbsjEarth中的对象
 * @param {string} reactProperty1 第一个绑定对象的属性名，必须是字符串类型，形式如：'prop1.prop2.prop3'
 * @param {object} reactObject2 第二个绑定对象，可以是vue对象，或者是XbsjEarth中的对象
 * @param {string} reactProperty2 第二个绑定对象的属性名，必须是字符串类型，形式如：'prop1.prop2.prop3'
 * @param {object} [params] 额外参数，暂时保留
 */
function track(reactObject1, reactProperty1, reactObject2, reactProperty2) {
  var _ref = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {},
    _ref$r2Tor = _ref.r2Tor1,
    r2Tor1 = _ref$r2Tor === undefined ? undefined : _ref$r2Tor,
    _ref$equals = _ref.equals,
    equals = _ref$equals === undefined ? undefined : _ref$equals;

  var l1 = new LocalObject(reactObject1, reactProperty1);
  var l2 = new LocalObject(reactObject2, reactProperty2);

  var disposers = [];

  if (l2.type === 'xbsjReact') {
    function reactFunc() {
      if (typeof l2.d === 'undefined') {
        l1.d = undefined;
      } else if (_types.isNativeArray(l2.d)) {
        // observe取代
        // arrayAssign(l1, l2, r2Tor1, equals);
        if (l2.d.length === l1.d.length) {
          arrayAssign(l1, l2, r2Tor1, equals);
          // if (l1.d.length >= 2 && l1.d[1] > 180.0) {
          //     debugger;
          // }
        }
      } else if (_types.isNative(l2.d)) {
        assign(l1, l2, r2Tor1, equals);
      } else {
        if (!Array.isArray(l2.d)) {
          l1.d = JSON.parse(JSON.stringify(l2.d));
        } else {
          // observe取代
          if (l2.d.length === l1.d.length) {
            // vue不能直接赋值，否则vue不响应！
            if (l1.type === 'vue') {
              l1.d = JSON.parse(JSON.stringify(l2.d));
            } else {
              l2.d.forEach(function(e, i) {
                return l1.d[i] = JSON.parse(JSON.stringify(e));
              });
            }
          }
        }
      }
    };

    // 检测l2.d有没发生变化，如果发生变化，就要更改intercep
    var oldL2d = undefined;
    var arrayDisposer = undefined;
    disposers.push(function() {
      return arrayDisposer = arrayDisposer && arrayDisposer();
    });

    function arrayReactFunc() {
      // 如果l2.d没有发生变化，则直接return
      if (oldL2d && l2.d === oldL2d) {
        return;
      }

      // 如果发生变化了l2.d不是原来的对象了，则取消监听！
      if (oldL2d) {
        arrayDisposer = arrayDisposer && arrayDisposer();
        oldL2d = undefined;
      }

      if (!Array.isArray(l2.d)) {
        return;
      }

      // 重新监听新的对象！通过observe对象修改数组，不需要reactFunc了！
      if (!Array.isArray(l1.d)) {
        l1.d = [];
      }

      oldL2d = l2.d;

      if (Array.isArray(l1.d) && l1.d.length !== l2.d.length) {
        var _l1$d3;

        scratchArrayForArrayAssign.length = oldL2d.length;
        var r2_ = r2Tor1 ? r2Tor1(oldL2d, scratchArrayForArrayAssign) : oldL2d;
        // 如果r2_中的元素是对象，则需要json方式克隆一下！
        r2_.forEach(function(e, i) {
          !_types.isNative(e) && (r2_[i] = JSON.parse(JSON.stringify(e)));
        });
        (_l1$d3 = l1.d).splice.apply(_l1$d3, [0, l1.d.length, ...r2_]);
      }

      arrayDisposer = observe(oldL2d, function(changed) {
        if (changed.type === 'splice') {
          var _l1$d4;

          // 没有考虑added和removed都是NaN的情况，此种情况会导致死循环！// vtxf 20200330
          // if (changed.added && changed.removed && changed.added.length === changed.removed.length &&
          //     changed.added.every((e, i) => changed.removed[i] === e)) {
          //         return;
          // }
          if (changed.added && changed.removed && changed.added.length === changed.removed.length &&
            changed.added.every(function(e, i) {
              return changed.removed[i] === e || isNaN(changed.removed[i]) && isNaN(e);
            })) {
            return;
          }

          // 避免bind，即相互track的元素形成互相add的死循环！！
          // 这里判断如果数组相同，说明不需要继续splice了！！因为changed.object本身就是变化后的。
          // const r2_ = r2Tor1 ? r2Tor1(changed.object, scratchArrayForArrayAssign) : changed.object;
          scratchArrayForArrayAssign.length = oldL2d.length;
          var _r2_ = r2Tor1 ? r2Tor1(oldL2d, scratchArrayForArrayAssign) : (scratchArrayForArrayAssign
            .splice.apply(scratchArrayForArrayAssign, [0, oldL2d.length, , ...oldL2d]),
            scratchArrayForArrayAssign);

          var equal = false;
          if (equals) {
            equal = equals(l1.d, _r2_);
          } else {
            equal = _r2_.length === l1.d.length && _r2_.every(function(e, i) {
              if (_types.isNative(l1.d[i])) {
                return l1.d[i] === e;
              } else {
                return JSON.stringify(l1.d[i]) === JSON.stringify(e);
              }
            });
          }

          if (equal) {
            return;
          }

          _r2_.length = changed.index + changed.added.length; // 删除尾部元素
          _r2_.splice(0, changed.index); // 删除首部元素，剩下的就是修改了的元素
          // 如果r2_中的元素是对象，则需要json方式克隆一下！
          _r2_.forEach(function(e, i) {
            !_types.isNative(e) && (_r2_[i] = JSON.parse(JSON.stringify(e)));
          });
          var index = changed.index;
          var removedCount = changed.removedCount;
          (_l1$d4 = l1.d).splice.apply(_l1$d4, [index, removedCount, ..._r2_]);
          // console.log('---');
        }
      });
    };

    arrayReactFunc();
    reactFunc();

    disposers.push(reaction(function() {
      return l2.d;
    }, arrayReactFunc));
    disposers.push(reaction(function() {
      if (typeof l2.d === 'undefined') {
        return undefined;
      } else if (_types.isNativeArray(l2.d)) {
        return [...l2.d];
        // return [...l2.d]; // 不创建新数组，但是不执行是否会有别的问题 vtxf
        // 有问题，会导致不更新！
        // const na = scratchEqualArray;
        // na.splice(0, na.length, ...l2.d);
        // return na;
      } else if (_types.isNative(l2.d)) {
        return l2.d;
      } else {
        // return JSON.parse(JSON.stringify(l2.d)); // 这样返回会导致死循环，因为用不相等
        return JSON.stringify(l2.d); // 直接返回字符串！
      }
    }, reactFunc));
  } else if (l2.type === 'vue') {
    var _reactFunc = function _reactFunc() {
      if (typeof l2.d === 'undefined') {
        l1.d = undefined;
      } else if (_types.isNativeArray(l2.d)) {
        arrayAssign(l1, l2, r2Tor1, equals);
        // if (l1.d.length >= 2 && l1.d[1] > 1.0) {
        //     debugger;
        // }
      } else if (_types.isNative(l2.d)) {
        assign(l1, l2, r2Tor1, equals);
      } else {
        l1.d = JSON.parse(JSON.stringify(l2.d));
      }
    };

    // 强制执行一次！
    _reactFunc();
    disposers.push(reactObject2.$watch(reactProperty2, _reactFunc, {
      deep: true
    }));
  }

  return function() {
    disposers.forEach(function(d) {
      return d();
    });
    disposers.length = 0;
  };
}

export default track;
